
import axios,{AxiosError, AxiosInstance }  from 'axios'  //若AxiosInstance找不到  使用npm i axios --save-dev命令重新安装axios生成declare文件
//import { MessagePopup, MessagePopup } from 'element-plus';
 
import IPage from '../entity/common/response/IPage';
import ResultObjectC from '../entity/common/vo/ResultObjectC';
import {AppConfig} from 'public/config.js'   
import { MessagePopup } from '../utils/MessagePopup';
type ClassConstructor = {
    getInstanceByJSONObj (obj:any):any;
  };

type PathParameterType={
    newPath:string,
    pathRKeys:Set<string>
}  

type APIContent={
    gatewayRoutePath:string
    desc?:string
    methodType:string
    path:string
    parameterType:any
    resultObjectType:any
    innerClass?:ClassConstructor
    requestBody:boolean
}


export default class RequestUtil{
    //网关接口地址 引用自/public/config.js  
    public static serverAddr='http://192.168.2.226:9005/';   
   //nginx静态文件获取地址 引用自/public/config.js  
    public static serverStaticFileAddr= '' //AppConfig.STATIC_FILE_URL;  

    

    private static axiosInstance:AxiosInstance;

    /**axiosInstance初始化方法  */
    private static init(){
        if(this.axiosInstance!=null) return;
        //console.log("*****************AXIOS初始化***************")
        this.axiosInstance=axios.create()

        // 添加请求拦截器
        this.axiosInstance.interceptors.request.use(
            (config:any) => {
                //检查access_token是否失效 refresh_token还未失效尝试刷新token
                //TODO 。。。

                // 设置请求头token
                const token = uni.getStorageSync('token')
                if (token) {
                    config.headers!['Authorization'] = token;
                }
                return config;
            },
            (error:any) => {
                return Promise.reject(error);
            }
        );

        // 添加响应拦截器
        this.axiosInstance.interceptors.response.use(
            (response:any) => {
                // 正常返回ResultObjectC时候的code检查 统一弹窗提示错误
                const resData = response.data;
               // console.log("axios-interceptors-response-success: ",resData)
                if (resData.code && resData.code !== 0) {
                    MessagePopup.error(resData.message || resData.msg || '请求异常!');
                    uni.showToast({
                        icon: 'error',
                        title: resData.message || resData.msg || '请求异常!',
                        duration:500
                    });
                    return Promise.reject(resData);
                } 
                    
                return response;
            },
            (error:any) => {
                // 异常状态码处理
                let status=error.response?.status
                if(status && status==401){
                    //MessagePopup.error('401 Token认证失效');
                    //Session.clear(); // 清除浏览器全部临时缓存
                    uni.removeStorageSync('token')
                    // 回到登录页
                    MessagePopup.showOnlyConfirmModal('登录凭证失效，请重新登录!',() => { window.location.href = '/'  } )
                    return    
                }else if(status && status==404){
                    MessagePopup.error('404无资源');
                }else if(status && status==500){
                    MessagePopup.error('500服务器内部错误');
                }       
                MessagePopup.error('未知异常:'+error);
                return Promise.reject(error);
            }
        );

    }

    /**执行请求将结果转换为ResultObjctC对象实例 */
    static async executeRequestForResultObjectC<T>(api:APIContent,parameter?:object,pathParameter?:object):(Promise<ResultObjectC<T>>) {
        this.init()
        let response=(await this.axiosInstance(this.trsfAxiosFinalConfig(api,parameter,pathParameter))).data;
        if(api.innerClass!=undefined &&api.innerClass!=null ){
            //设置了innerClass直接尝试实例化泛型对象
            response.data=api.innerClass.getInstanceByJSONObj(response.data);
        }
        return response
    }

    /**执行请求将结果转换为ResultObjctC<R>.data对象实例 */
    static async executeRequestForObj<R>(api:APIContent,parameter?:object,pathParameter?:object):(Promise<R>) {
        this.init()
        if(api.innerClass==null || api.innerClass==undefined) throw api.path+",APIContent没有配置适合的innerClass请设置并满足类型<ClassConstructor>"
        let response= (await this.axiosInstance(this.trsfAxiosFinalConfig(api,parameter,pathParameter))).data;
        let tmpObj:any=response.data;
        let rsObj=api.innerClass?.getInstanceByJSONObj(tmpObj);
        return rsObj;
    }

    /**执行请求将结果转换为List<R>对象实例 */
    static async executeRequestForList<R>(api:APIContent,parameter?:object,pathParameter?:object):(Promise<R[]>) {
        this.init()
        if(api.innerClass==null || api.innerClass==undefined) throw api.path+",APIContent没有配置适合的innerClass请设置并满足类型<ClassConstructor>"
        let rsArr=<any>[]
        let response= (await this.axiosInstance(this.trsfAxiosFinalConfig(api,parameter,pathParameter))).data;
        let tmpList:any[]=response.data;
        tmpList && tmpList.forEach(c=>{
            rsArr.push(api.innerClass?.getInstanceByJSONObj(c))
        })
        return rsArr;
    }

    /**执行请求将结果转换为IPage<R>对象实例 (所有列表分页查询) */
    static async executeRequestForPage<R>(api:APIContent,parameter?:object,pathParameter?:object):(Promise<IPage<R>>) {
        this.init()
        if(api.innerClass==null || api.innerClass==undefined) throw api.path+",APIContent没有配置适合的innerClass请设置并满足类型<ClassConstructor>"
        let rsArr=<any>[]
        let rsPage:IPage<R>;
        let response= (await this.axiosInstance(this.trsfAxiosFinalConfig(api,parameter,pathParameter))).data;
        
        let pageObj=response.data;
        let tmpList:any[]=pageObj.records;
        tmpList.forEach(c=>{
            rsArr.push(api.innerClass?.getInstanceByJSONObj(c))
        })
        rsPage=IPage.getInstanceByParam(rsArr,pageObj.total,pageObj.current,pageObj.size,pageObj.pages) as IPage<R>;
        return rsPage;
    }

    /**
     * @name:  上传文件(如果response是文件流则下载错误文件）
     * @param parameter 请求参数 构建一个formData
     * 
     *  示例
     * 	const file = files.target.files[0];
        const formData = new FormData();
        formData.append("importFile", file);
        RequestUtil.uploadFile(BaseAPIConfig.importExcel,formData);
    */
    static async uploadFile(api:APIContent,parameter?:FormData,pathParameter?:object ):Promise<any> {
        this.init()
        let config:any=this.trsfAxiosFinalConfig(api,parameter,pathParameter)
        config["responseType"]="blob"
        config["timeout"]=2000 * 1000
        //config.headers! ={Authorization: Session.get('token')};

        return new Promise((resolve, reject) => {
            this.axiosInstance(config)
            .then(function (res) {
            let contentType=res.headers["content-type"]  
            if ( contentType === "application/octet-stream;charset=UTF-8" || contentType === "application/vnd.ms-excel;charset=UTF-8" ) {
                var content = res.data;
                var elink = document.createElement("a");
                elink.download = "错误文件.xlsx";
                elink.style.display = "none";
                var blob = new Blob([content]);
                elink.href = URL.createObjectURL(blob);
                document.body.appendChild(elink);
                elink.click();
                document.body.removeChild(elink);
                MessagePopup.error("导入数据监测到错误,请查看标记后的错误文件!")
                resolve(content); //下载错误xlsx的处理
            } else {
                //将blob转回字符串判断
                var reader: any = new FileReader();
                reader.onload = function (event:any) {
                var content = JSON.parse(reader.result); //内容就在这里
                // console.log('处理结果',content)
                MessagePopup.success(content.message)
                resolve(content); //后台返回json报错信息处理
                
                };
                reader.readAsText(res.data);
            }
            })
            .catch((err) => {
                MessagePopup.error(err.message)
                reject(err);
            });
        });
    }
  
   
  /**
   * @name: 下载文件
   * 
   * @param parameter 请求参数
   * @param fileName 下载文件名称取别名（当请求header无法解析出下载的文件名时使用此名称）
   * 
   */
  static async  downloadFile(api:APIContent,fileName?:string,parameter?:object,pathParameter?:object):Promise<any>  {
        this.init()
        let config:any=this.trsfAxiosFinalConfig(api,parameter,pathParameter)
        config["responseType"]="blob"
        //config["responseType"]="arraybuffer"
        config["timeout"]=2000 * 1000
        //config.headers! ={Authorization: Session.get('token')};

        return new Promise((resolve, reject) => {    
            this.axiosInstance(config)
            .then(response => {
              let contentType=response.headers["content-type"]  
              if(contentType === "application/octet-stream;charset=UTF-8" || contentType === "application/vnd.ms-excel;charset=UTF-8"){
                    let contentDisposition = decodeURI(response.headers["content-disposition"]);
                    let fileRegex = /([^(filename=)]+\.xlsx?)$/g;
                    let result = contentDisposition.match(fileRegex);
                    let fFileName=(result && result.length>0)?result[0]:(fileName || "未知类型文件")
                    const url = window.URL.createObjectURL(new Blob([response.data]));
                    const link = document.createElement('a');
                    link.href = url;
                    link.style.display = 'none';
                    link.setAttribute('download', fFileName);
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    window.URL.revokeObjectURL(url);
                    MessagePopup.success("开始下载文件。")
                    resolve("下载成功"); //错误处理
               }else{
                  //JSON异常信息等解析
                  var reader: any = new FileReader();
                  reader.onload = function (event:any) {
                          var content = JSON.parse(reader.result); //内容就在这里
                          MessagePopup.error((content as ResultObjectC<string>).message)
                          reject((content as ResultObjectC<string>).message); //错误处理
                      };
                  reader.readAsText(response.data);
               }
            })
            .catch(error => {
                MessagePopup.error("下载异常!")
                reject(error);
            });
        });

    
   }
   
   
   //-------------private-----------------

   /** apiconfig+参数  转换出最终axios可直接使用的config*/
   private static trsfAxiosFinalConfig(api:APIContent,parameter?:object,pathParameter?:object):any{
        if(parameter==null || parameter==undefined) parameter={};
        //api.methodType= api.methodType.toLowerCase()
        let requestConfig:any={...{},...api}
        let trsfPathObj:PathParameterType=this.trsfPathPlaceholder(api.path,pathParameter!=undefined?pathParameter:parameter);
        requestConfig.url=this.serverAddr+api.gatewayRoutePath.toLowerCase()+trsfPathObj.newPath;
        requestConfig.method=api.methodType.toLowerCase()
        if(api.parameterType && api.requestBody==true ){   //api.parameterType.includes(":RequestBody")
            requestConfig.contentType="application/json";
            requestConfig.data=parameter;
        }else if(api.parameterType && api.parameterType.includes(":MultipartFile")){
            requestConfig.contentType="multipart/form-data";
            requestConfig.data=parameter;  
        }else{
            requestConfig.contentType="application/x-www-form-urlencoded";
            //参数中排除掉路径{xid}的值  为了真实路径不要携带不必要参数 
            for (let c of trsfPathObj.pathRKeys) {delete (parameter as any)[c]}    
            requestConfig.params=parameter;
        }    
        //console.log("trsfAxiosFinalConfig:",requestConfig)
        return requestConfig
    }

    /**转换路径中/{xid}替换为真实的参数值 */
    private static trsfPathPlaceholder(requestPath:string,parameter:any):PathParameterType{
        if(!requestPath.includes("{")) return  {newPath:requestPath,pathRKeys:new Set()};
        let rs=requestPath;
        let pathRKeySet:Set<string>=new Set();
        for(let key in parameter){
            if(rs.indexOf("{"+key+"}")==-1) continue;
            rs=rs.replace("{"+key+"}",parameter[key]);
            pathRKeySet.add(key);
        }
        return  {newPath:rs,pathRKeys:pathRKeySet};
    }
  


}